use std::fs;
use std::path::Path;

use reqwest::Url;

use crate::index::version::Version;
use crate::runtime::runtime::error::InternalError;
use crate::runtime::runtime::path;
use crate::runtime::runtime::result::InternalErrorResult;
use crate::util::util::{archive, system};
use crate::util::util::network::{downloader, network};
use crate::util::util::system::syscall;

pub struct Installer<'a> {
    name: &'a str,
    version: &'a Version,
    source_only: bool,
}

impl<'a> Installer<'a> {
    pub fn new(name: &'a str, version: &'a Version, source_only: bool) -> Self {
        Installer { name, version, source_only }
    }

    /// 包装方法
    pub fn install(name: &'a str, version: &'a Version, source_only: bool) -> InternalErrorResult<()> {
        Installer::new(name, version, source_only).run()
    }

    /// 执行安装动作
    pub fn run(&mut self) -> InternalErrorResult<()> {
        if self.source_only && !self.version.source.enable {
            return Err(InternalError::new("此工具未启用源码安装".to_string()));
        }

        let p = Path::new(path::DEM_SOFTWARE_PATH).join(self.name).join(self.version.version.as_str());
        let target = p.as_path();

        let result = self.init(target, "installing");
        if result.is_err() {
            self.clear();

            return Err(result.unwrap_err());
        }

        // 若指定仅从源码安装则跳过归档安装方式
        if !self.source_only && self.version.archive.enable {
            let package = self.version.archive.package.replace("{ROOT}", target.to_str().unwrap()).replace("{VERSION}", self.version.version.as_str());
            let package = Url::parse(package.as_str()).map_err(|e| InternalError::new(format!("Could not parse archive package: {}", e)));
            if package.is_err() {
                self.clear();

                return Err(package.unwrap_err());
            }

            let check = network::check_remote_file_exists(package.unwrap());
            if check.is_err() {
                self.clear();

                return Err(check.unwrap_err());
            }

            if check.unwrap() {
                let result = self.archive(target, self.version);
                if result.is_ok() || (result.is_err() && !self.version.source.enable) {
                    if result.is_err() {
                        self.clear();

                        return Err(result.unwrap_err());
                    }

                    let result = self.switch_state(target, "installing", "installed");
                    if result.is_err() {
                        self.clear();

                        return Err(result.unwrap_err());
                    }

                    return Ok(());
                }

                println!("预构建包安装失败，该工具支持源码安装，将从源码编译安装")
            }
        }

        if self.version.source.enable {
            let result = self.source(target, self.version);
            if result.is_err() {
                self.clear();

                return Err(result.unwrap_err());
            }

            let result = self.switch_state(target, "installing", "installed");
            if result.is_err() {
                self.clear();

                return Err(result.unwrap_err());
            }

            return Ok(());
        }

        self.clear();

        Err(InternalError::new("此工具没有有效的安装方式".to_string()))
    }
}

impl<'a> Installer<'a> {
    /// 初始化安装的进度，重建目标目录并将安装状态切换为指定状态
    fn init<P: AsRef<Path>>(&self, target: P, state: &str) -> InternalErrorResult<()> {
        system::fs::remove_dir_all(target.as_ref())?;
        fs::create_dir_all(target.as_ref()).map_err(|e| InternalError::new(format!("Could not create target path: {} {}", target.as_ref().to_str().unwrap(), e)))?;

        self.switch_state(target.as_ref(), "", state)
    }

    /// 切换安装状态
    fn switch_state<P: AsRef<Path>>(&self, target: P, from: &str, to: &str) -> InternalErrorResult<()> {
        if "" != from {
            let result = fs::remove_file(target.as_ref().join(format!(".{}", from)));
            if result.is_err() {
                return Err(InternalError::new(format!("移除文件失败: {} {}", target.as_ref().to_str().unwrap(), result.unwrap_err())));
            }
        }

        let result = fs::write(target.as_ref().join(format!(".{}", to)), "1");
        if result.is_err() {
            return Err(InternalError::new(format!("写入文件失败: {} {}", target.as_ref().to_str().unwrap(), result.unwrap_err())));
        }

        Ok(())
    }

    /// 归档的方式安装
    fn archive<P: AsRef<Path>>(&self, target: P, version: &Version) -> InternalErrorResult<()> {
        for script in version.archive.script.install.before.iter() {
            let script = path::replace_package_tokens(script, target.as_ref(), version.version.as_str(), None);
            syscall::execute_system_command(script.as_str(), Some(target.as_ref()), None)?;
        }

        // 下载安装
        let package = path::replace_package_tokens(version.archive.package.as_str(), target.as_ref(), version.version.as_str(), None);
        let (file, size) = downloader::download(Url::parse(package.as_str()).map_err(|e| InternalError::new(format!("Could not parse package: {}", e)))?)?;

        if package.ends_with(".tar.gz") {
            archive::gzip::decompress(file.path(), &file, size, target.as_ref())?;
        } else if package.ends_with(".tar.xz") {
            archive::xz::decompress(file.path(), &file, size, target.as_ref())?;
        } else if package.ends_with(".zip") {
            archive::zip::decompress(file.path(), &file, size, target.as_ref())?;
        } else {
            return Err(InternalError::new(format!("Could not decompress package. unknown file ext: {}", package.as_str())));
        }

        for script in version.archive.script.install.after.iter() {
            let script = path::replace_package_tokens(script, target.as_ref(), version.version.as_str(), None);
            syscall::execute_system_command(script.as_str(), Some(target.as_ref()), None)?;
        }

        Ok(())
    }

    /// 源码的方式安装
    /// 存在递归
    fn source<P: AsRef<Path>>(&self, target: P, version: &Version) -> InternalErrorResult<()> {
        // 处理依赖
        for depend in version.source.depends.iter() {
            let dp = target.as_ref().join("depends");

            if depend.archive.enable {
                self.archive(dp.to_str().unwrap(), depend)?;
            } else if depend.source.enable {
                self.source(dp.to_str().unwrap(), depend)?;
            }
        }

        let package = path::replace_package_tokens(version.source.package.as_str(), target.as_ref(), version.version.as_str(), None);
        let (file, size) = downloader::download(Url::parse(package.as_str()).map_err(|e| InternalError::new(format!("Could not parse package: {}", e)))?)?;

        // 解压到临时文件夹
        let temp_dir = tempfile::tempdir_in(target.as_ref()).map_err(|e| InternalError::new(format!("Could not create temporary directory: {}", e)))?;
        let temp_dir = temp_dir.path();

        if package.ends_with(".tar.gz") {
            archive::gzip::decompress(file.path(), &file, size, temp_dir)?;
        } else if package.ends_with(".tar.xz") {
            archive::xz::decompress(file.path(), &file, size, temp_dir)?;
        } else if package.ends_with(".zip") {
            archive::zip::decompress(file.path(), &file, size, temp_dir)?;
        } else {
            return Err(InternalError::new(format!("Could not decompress package. unknown file ext: {}", package.as_str())));
        }

        // 编译
        for script in version.source.build.chains.iter() {
            let script = path::replace_package_tokens(script, target.as_ref(), version.version.as_str(), Some(temp_dir.to_str().unwrap()));
            syscall::execute_system_command(script.as_str(), Some(target.as_ref()), None)?;
        }

        Ok(())
    }

    /// 安装失败时的清理工作
    fn clear(&self) {
        let p = Path::new(path::DEM_SOFTWARE_PATH).join(self.name).join(self.version.version.as_str());
        let _ = system::fs::remove_dir_all(p.to_str().unwrap());
    }
}
